package org.testmyapp.persistence;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;

import org.testmyapp.entities.AccountingMedical;
import org.testmyapp.entities.Contract;
import org.testmyapp.entities.ProcessEntityKey;

@Named
@SessionScoped
public class BusinessObjectPersistenceProvider implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -2601615431169339149L;
	private static final String persistenceUnit = "customerPersistenceUnit";

	private EntityManagerFactory emf;
	private EntityManager em;

	/**
	 * A whole transaction is used to persist Object.
	 * 
	 * @param bo
	 *            (BusinessObjectMarker)
	 * @return boolean whether "bo" was persisted or not
	 */
	public boolean persist(BusinessObjectMarker bo) {
		initEntityManager();
		EntityTransaction transaction = em.getTransaction();
		transaction.begin();
		try {
			em.persist(bo);
		} catch (Exception e) {
			transaction.rollback();
			System.err.println("technical exception @persist bro!");
			e.printStackTrace(System.err);
			return false;
		}
		transaction.commit();
		return true;
	}
	
	/**
	 * A whole transaction is used to persist a list of Objects.
	 * 
	 * @param boList
	 *            (BusinessObjectMarker)
	 * @return boolean whether "boList" was persisted or not
	 */
	public boolean persistList(List<BusinessObjectMarker> boList) {
		initEntityManager();
		EntityTransaction transaction = em.getTransaction();
		transaction.begin();
		try {
			for(BusinessObjectMarker bom : boList) {
				em.persist(bom);
			}
		} catch (Exception e) {
			transaction.rollback();
			System.err.println("technical exception @persist bro!");
			e.printStackTrace(System.err);
			return false;
		}
		transaction.commit();
		return true;
	}
	
	public boolean merge(Object bo) {
		initEntityManager();
		EntityTransaction transaction = em.getTransaction();
		if(!(bo instanceof BusinessObjectMarker || bo instanceof ConfigObjectMarker)) {
			throw new IllegalArgumentException("Object " + bo + " is not config nor business");
		}
		transaction.begin();
		try {
			em.merge(bo);
		} catch (Exception e) {
			transaction.rollback();
			System.err.println("technical exception @merge bro!");
			e.printStackTrace(System.err);
			return false;
		}
		transaction.commit();
		return true;
	}

	private void initEntityManager() {
		if (emf == null) {
			emf = Persistence.createEntityManagerFactory(persistenceUnit);
		}
		if (em == null) {
			em = emf.createEntityManager();
		}
	}

	public <T> T find(Class T, Object argument) {
		initEntityManager();
		EntityTransaction transaction = em.getTransaction();
		transaction.begin();
		try {
			T result = (T) em.find(T, argument);
			transaction.commit();
			return result;
		} catch (Exception e) {
			transaction.rollback();
			return null;
		}
	}
	
	public <T> List<T> queryList(String sql,Map<String, Object> params,Class T) {
		Query query;
		initEntityManager();
		try {
		query = em.createQuery(sql);
		System.out.println(query + " executed");
		} catch (Exception e) {
			System.err.println(sql + " " + params + " technical exception bro!");
			e.printStackTrace(System.err);
			return null;
		}
		for(Entry<String, Object> s : params.entrySet()) {
			query.setParameter(s.getKey(), s.getValue());
		}
		List<T> result = (List<T>) query.getResultList();
		return result;
	}
	
	public <T> T querySingle(String sql,Map<String, Object> params,Class T) {
		Query query;
		initEntityManager();
		try {
		query = em.createQuery(sql);
		System.out.println(query + " executed");
		} catch (Exception e) {
			System.err.println(sql + " " + params + " technical exception bro!");
			e.printStackTrace(System.err);
			return null;
		}
		for(Entry<String, Object> s : params.entrySet()) {
			query.setParameter(s.getKey(), s.getValue());
		}
		try {
			T result = (T) query.getSingleResult();
			return result;
		}
		catch (Exception e) {
			System.err.println("error gettin single result: " + sql + " , " + params);
			return null;
		}
	}
	
	public Integer getKeyForProcessEntity(String entityName) {
		String sql = "select a from " + ProcessEntityKey.tableName
				+ " a where a.entityName = :key";
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("key", entityName);
		
		ProcessEntityKey peKey = this.querySingle(sql, params, ProcessEntityKey.class);
		if (peKey == null) {
			return null;
		}
		peKey.setKeyValue(peKey.getKeyValue() + 1);
		this.merge(peKey);
		return peKey.getKeyValue();
	}

}
